package com.song.codesandbox.utils;

import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.StrUtil;
import com.song.codesandbox.model.ExecuteMessage;
import com.song.codesandbox.model.ProcessExecType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.StopWatch;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 进程运行工具类
 */
@Slf4j
public class ProcessUtils {

    /**
     * 运行进程，并返回执行结果
     *
     * @param process         运行的进程
     * @param processExecType 运行的类型（编译/执行）
     * @return
     */
    public static ExecuteMessage runProcess(Process process, ProcessExecType processExecType) {
        ExecuteMessage executeMessage = new ExecuteMessage();

        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 运行程序
            int exitValue = process.waitFor();
            stopWatch.stop();

            long time = stopWatch.getLastTaskTimeMillis();
            executeMessage.setTime(time);
            executeMessage.setExitValue(exitValue);

            // 处理运行结果
            if (exitValue == 0) { // 运行成功
                log.info("{}运行成功, 结果码: {}", processExecType.getInfo(), exitValue);
                // 获取程序的正常输出
                String successOutputMessage = getOutputMessageByInputStream(process.getInputStream());
                executeMessage.setMessage(successOutputMessage);
            } else { // 运行失败
                log.info("{}运行失败, 错误码: {}", processExecType.getInfo(), exitValue);
                // 获取程序的错误输出
                String errorOutputMessage = getOutputMessageByInputStream(process.getErrorStream());
                executeMessage.setErrorMessage(errorOutputMessage);

                // 获取程序的正常输出
                String successOutputMessage = getOutputMessageByInputStream(process.getInputStream());
                executeMessage.setMessage(successOutputMessage);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        log.info("{}结果: {}", processExecType.getInfo(), executeMessage);
        return executeMessage;
    }


    /**
     * 运行交互式进程，并返回执行结果
     *
     * @param process         运行的进程
     * @param args            交互式的参数
     * @param processExecType 运行的类型（编译/执行）
     * @return
     */
    public static ExecuteMessage runProcess(Process process, String args, ProcessExecType processExecType) {
        ExecuteMessage executeMessage = new ExecuteMessage();

        try {
            // 向控制台输入数据
            InputStream inputStream = process.getInputStream();
            OutputStream outputStream = process.getOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
            String[] split = args.split(" ");
            outputStreamWriter.write(StrUtil.join("\n", split) + "\n");
            outputStreamWriter.flush(); // 相当于按下回车

            // 分批获取程序的正常输出
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuffer successOutputBuilder = new StringBuffer();
            String compileOutputLine;
            while ((compileOutputLine = bufferedReader.readLine()) != null) {
                successOutputBuilder.append(compileOutputLine);
            }
            executeMessage.setMessage(successOutputBuilder.toString());

            // 资源回收
            outputStreamWriter.close();
            outputStream.close();
            inputStream.close();
            process.destroy();
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("{}结果: {}", processExecType.getInfo(), executeMessage);
        return executeMessage;
    }


    /**
     * 运行指定命令，并返回执行结果
     *
     * @param cmd             运行的命令
     * @param processExecType 运行的类型（编译/执行）
     * @return
     */
    public static ExecuteMessage runProcess(String cmd, ProcessExecType processExecType) {
        try {
            return runProcess(Runtime.getRuntime().exec(cmd), processExecType);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 根据[输入流]获取[输出信息]
     *
     * @param inputStream 输入流
     * @return 输出信息
     * @throws IOException
     */
    private static String getOutputMessageByInputStream(InputStream inputStream) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String outputLine;
        List<String> outputStrList = new ArrayList<>();
        while ((outputLine = bufferedReader.readLine()) != null) {
            outputStrList.add(outputLine);
        }
        return StringUtils.join(outputStrList, "\n");
    }

    /**
     * 程序超时监控
     * - 创建一个守护线程, 超时时直接终端进程
     *
     * @param process 需要监控的程序
     * @param timeout 程序超时时间
     */
    public static void timeoutMonitor(Process process, long timeout) {
        new Thread(() -> {
            try {
                Thread.sleep(timeout);
                if (process.isAlive()) {
                    log.info("进程超时，已终止");
                    process.destroy();
                }
            } catch (InterruptedException e) {
                // todo 可以抛出程序异常, 返回前端
                e.printStackTrace();
            }
        }).start();
    }
}
